package service

import (
	"context"
	"sale-admin/config/mysql"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/devicec"
	"sale-admin/internal/app/grpc/protoc/system_admin"
	"sale-admin/internal/app/web/service/common"
	"sale-admin/internal/dal"
	"sale-admin/internal/dao"
	"sale-admin/internal/models"
	"sort"
	"strconv"
	"strings"

	"192.168.1.75/go-pkg/logx"
	"github.com/golang-module/carbon/v2"
	"github.com/samber/lo"
	"gorm.io/gorm"
)

type RobotDailyReport struct{}

type RobotInfo struct {
	MerchantID  string `json:"merchant_id"`
	RobotName   string `json:"robot_name"`
	SalesID     string `json:"sales_id"`
	LocationID  string `json:"location_id"`
	OpeningFlag string `json:"opening_flag"`
	OpeningTime string `json:"opening_time"`
	ProvinceID  string `json:"province_id"`
	CityID      string `json:"city_id"`
	DistrictID  string `json:"district_id"`
}

type TaskStruct struct {
	TotalTaskOdometer       int64
	TotalTaskDuration       int64
	TotalTaskRunCount       int64
	TotalChargingDuration   int64
	TotalIdleDuration       int64
	TotalToTaskDuration     int64
	TotalToChargingDuration int64
	TotalFaultDuration      int64
}

type RobotNameSalesInfoList struct {
	TotalGoodsInventory    int64 `json:"total_goods_inventory"`
	TotalGoodsMaxInventory int64 `json:"total_goods_max_inventory"`
}

// 数据填充
func (RobotDailyReport) FixData(totalStart, totalEnd int64) (err error) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()
		dateStart := carbon.Parse(date).StartOfDay().ToDateTimeString()
		dateEnd := carbon.Parse(date).EndOfDay().ToDateTimeString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					MerchantID:  Robot.MerchantID,
					RobotName:   Robot.RobotName,
					SalesID:     Robot.SalesID,
					LocationID:  strconv.Itoa(Robot.Areas3),
					OpeningFlag: Robot.OpeningFlag,
					OpeningTime: Robot.OpeningTime,
					ProvinceID:  strconv.Itoa(Robot.Areas1),
					CityID:      strconv.Itoa(Robot.Areas2),
					DistrictID:  strconv.Itoa(Robot.Areas5),
				}
			}
		}
		// 记录库存信息
		SalesInfoList, _ := dao.RobotSalesInfo{}.GetRobotInventoryList(ctx, db, date)
		SalesInfoListMap := make(map[string]RobotNameSalesInfoList)
		for _, v := range SalesInfoList {
			SalesInfoListMap[v.RobotName] = RobotNameSalesInfoList{
				TotalGoodsInventory:    int64(v.GoodsInventory),
				TotalGoodsMaxInventory: int64(v.GoodsMaxInventory),
			}
		}
		// 运营事件
		EventList, _ := dao.Event{}.GetEventList(ctx, db, dateStart, dateEnd)
		EventMap := make(map[string]dao.SalesIDEventList)
		for _, v := range EventList {
			EventMap[v.RobotName] = v
		}

		for _, v := range RobotInfoMap {
			// 运营数据
			deviceAdminClient, err := client.GetDeviceAdminClient()
			RobotTask := TaskStruct{}
			if err == nil {
				var req devicec.DeviceTaskInfoReq
				req.IdentificationCode = v.SalesID
				req.Date = date
				res, resErr := deviceAdminClient.DeviceTaskInfo(context.Background(), &req)
				if resErr == nil && res.Code != -1 {
					RobotTask = TaskStruct{
						TotalTaskOdometer:     res.TaskInfo.TotalTaskOdometer,
						TotalTaskDuration:     res.TaskInfo.TotalTaskDuration,
						TotalTaskRunCount:     res.TaskInfo.TotalTaskRunCount,
						TotalChargingDuration: res.TaskInfo.TotalChargingDuration,
						TotalIdleDuration:     res.TaskInfo.TotalIdleDuration,
					}
				}
			}
			opt := []dao.RobotDailyReportOption{}
			// 新城新店
			opt = append(opt, dao.RobotDailyReport{}.WithOpeningFlag(v.OpeningFlag))
			opt = append(opt, dao.RobotDailyReport{}.WithOpeningTime(v.OpeningTime))
			// 商品库存
			if _, ok := SalesInfoListMap[v.RobotName]; ok {
				opt = append(opt, dao.RobotDailyReport{}.WithGoodsInventory(SalesInfoListMap[v.RobotName].TotalGoodsInventory))
				opt = append(opt, dao.RobotDailyReport{}.WithGoodsMaxInventory(SalesInfoListMap[v.RobotName].TotalGoodsInventory))
			}
			// 运行时长
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskOdometer(RobotTask.TotalTaskOdometer))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskDuration(RobotTask.TotalTaskDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskRunCount(RobotTask.TotalTaskRunCount))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalChargingDuration(RobotTask.TotalChargingDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalIdleDuration(RobotTask.TotalIdleDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalToTaskDuration(RobotTask.TotalToTaskDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalToChargingDuration(RobotTask.TotalToChargingDuration))
			if carbon.CreateFromTimestamp(i).ToStdTime().Before(carbon.Now().ToStdTime()) { // 历史数据统计异常时长
				if RobotTask.TotalTaskOdometer != -1 && RobotTask.TotalTaskDuration != -1 {
					// 异常时长=24小时-出门时长-任务时长-回充时长-充电时长-待机时长
					RobotTask.TotalFaultDuration = 24*3600 -
						(RobotTask.TotalToTaskDuration)/1000 -
						(RobotTask.TotalTaskDuration)/1000 -
						(RobotTask.TotalToChargingDuration)/1000 -
						RobotTask.TotalChargingDuration -
						RobotTask.TotalIdleDuration
					opt = append(opt, dao.RobotDailyReport{}.WithTotalFaultDuration(RobotTask.TotalFaultDuration))
				}
			}
			operationDuration := RobotTask.TotalTaskDuration
			if _, ok := EventMap[v.RobotName]; ok {
				operationDuration = RobotTask.TotalTaskDuration - int64(EventMap[v.RobotName].AffectedTimeMill)
				if operationDuration < 0 {
					operationDuration = 0
				}
			}
			opt = append(opt, dao.RobotDailyReport{}.WithTotalOperationDuration(operationDuration))
			// 修复历史数据时，可能merchant_id等信息与当时的机器信息不一致
			dao.RobotDailyReport{}.Save(ctx, db, v.MerchantID, v.RobotName, date,
				v.ProvinceID, v.CityID, v.DistrictID, v.LocationID,
				opt...,
			)
			// 曝光量
			RobotDailyReport{}.UpdateFaceCount(ctx, db, v.RobotName, date)
			// 凝视数据
			RobotDailyReport{}.UpdateStareCount(ctx, db, v.RobotName, date)
			// 商品点击量
			RobotDailyReport{}.UpdateGoodsClick(ctx, db, v.RobotName, date)
			// 屏幕点击量
			RobotDailyReport{}.UpdateScreenClick(ctx, db, v.RobotName, date)
			// 订单信息
			RobotDailyReport{}.UpdateOrder(ctx, db, v.RobotName, date)
		}
	}
	return
}

// 修复任务数据
func (RobotDailyReport) FixTask(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()
		dateStart := carbon.Parse(date).StartOfDay().ToDateTimeString()
		dateEnd := carbon.Parse(date).EndOfDay().ToDateTimeString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					MerchantID:  Robot.MerchantID,
					RobotName:   Robot.RobotName,
					SalesID:     Robot.SalesID,
					LocationID:  strconv.Itoa(Robot.Areas3),
					OpeningFlag: Robot.OpeningFlag,
					OpeningTime: Robot.OpeningTime,
					ProvinceID:  strconv.Itoa(Robot.Areas1),
					CityID:      strconv.Itoa(Robot.Areas2),
					DistrictID:  strconv.Itoa(Robot.Areas5),
				}
			}
		}
		// 运营事件
		EventList, _ := dao.Event{}.GetEventList(ctx, db, dateStart, dateEnd)
		EventMap := make(map[string]dao.SalesIDEventList)
		for _, v := range EventList {
			EventMap[v.RobotName] = v
		}

		for _, v := range RobotInfoMap {
			// 运营数据
			deviceAdminClient, err := client.GetDeviceAdminClient()
			RobotTask := TaskStruct{}
			if err == nil {
				var req devicec.DeviceTaskInfoReq
				req.IdentificationCode = v.SalesID
				req.Date = date
				res, resErr := deviceAdminClient.DeviceTaskInfo(context.Background(), &req)
				if resErr == nil && res.Code != -1 && res.TaskInfo != nil {
					logx.Info(context.Background(), "DeviceTaskInfo", logx.Any("res", res))
					RobotTask = TaskStruct{
						TotalTaskOdometer:       res.TaskInfo.TotalTaskOdometer,
						TotalTaskDuration:       res.TaskInfo.TotalTaskDuration,
						TotalTaskRunCount:       res.TaskInfo.TotalTaskRunCount,
						TotalChargingDuration:   res.TaskInfo.TotalChargingDuration,
						TotalIdleDuration:       res.TaskInfo.TotalIdleDuration,
						TotalToTaskDuration:     res.TaskInfo.TotalToTaskDuration,
						TotalToChargingDuration: res.TaskInfo.TotalToChargingDuration,
					}
				}
				if resErr != nil {
					logx.Error(context.Background(), "Get DeviceTaskInfo err", logx.Err(resErr), logx.Any("res", res))
				}
			}
			if err != nil {
				logx.Error(context.Background(), "GetDeviceAdminClient err", logx.Err(err))
			}
			opt := []dao.RobotDailyReportOption{}
			// 运行时长
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskOdometer(RobotTask.TotalTaskOdometer))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskDuration(RobotTask.TotalTaskDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskRunCount(RobotTask.TotalTaskRunCount))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalChargingDuration(RobotTask.TotalChargingDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalIdleDuration(RobotTask.TotalIdleDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalToTaskDuration(RobotTask.TotalToTaskDuration))
			opt = append(opt, dao.RobotDailyReport{}.WithTotalToChargingDuration(RobotTask.TotalToChargingDuration))
			if carbon.CreateFromTimestamp(i).ToStdTime().Before(carbon.Now().ToStdTime()) { // 历史数据统计异常时长
				if RobotTask.TotalTaskOdometer != -1 && RobotTask.TotalTaskDuration != -1 {
					// 异常时长=24小时-出门时长-任务时长-回充时长-充电时长-待机时长
					RobotTask.TotalFaultDuration = 24*3600 -
						(RobotTask.TotalToTaskDuration)/1000 -
						(RobotTask.TotalTaskDuration)/1000 -
						(RobotTask.TotalToChargingDuration)/1000 -
						RobotTask.TotalChargingDuration -
						RobotTask.TotalIdleDuration
					opt = append(opt, dao.RobotDailyReport{}.WithTotalFaultDuration(RobotTask.TotalFaultDuration))
				}
			}
			operationDuration := RobotTask.TotalTaskDuration
			if _, ok := EventMap[v.RobotName]; ok {
				operationDuration = RobotTask.TotalTaskDuration - int64(EventMap[v.RobotName].AffectedTimeMill)
				if operationDuration < 0 {
					operationDuration = 0
				}
			}
			opt = append(opt, dao.RobotDailyReport{}.WithTotalOperationDuration(operationDuration))
			// 修复历史数据时，可能merchant_id等信息与当时的机器信息不一致
			dao.RobotDailyReport{}.Save(ctx, db, v.MerchantID, v.RobotName, date,
				v.ProvinceID, v.CityID, v.DistrictID, v.LocationID,
				opt...,
			)
		}
	}
}

// 修复机器位置
func (RobotDailyReport) FixRobotLocation(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					RobotName: Robot.RobotName,
					SalesID:   Robot.SalesID,
				}
			}
		}

		for _, v := range RobotInfoMap {
			// 当日有订单
			RobotOrderList, _ := dao.NewOrder(ctx, dal.Q).WithSalesIDCond([]string{v.SalesID}).WithDateCond(date).OrderRobot()
			if len(RobotOrderList) > 0 {
				provinceID := RobotOrderList[0].Areas1
				cityID := RobotOrderList[0].Areas2
				locationID := RobotOrderList[0].Areas3
				districtID := RobotOrderList[0].Areas5
				dao.RobotDailyReport{}.Save(ctx, db, RobotOrderList[0].MerchantID, RobotOrderList[0].RobotName, date,
					provinceID, cityID, districtID, locationID,
				)
			} else {
				// 今日无订单，获取最近订单
				recentOrder := dao.NewOrder(ctx, dal.Q).WithSalesIDCond([]string{v.SalesID}).RobotRecentOrder(date)
				if recentOrder.Areas3 != "" {
					provinceID := recentOrder.Areas1
					cityID := recentOrder.Areas2
					districtID := recentOrder.Areas5
					locationID := recentOrder.Areas3
					dao.RobotDailyReport{}.Save(ctx, db, recentOrder.MerchantID, v.RobotName, date,
						provinceID, cityID, districtID, locationID,
					)
				}
			}
		}
	}
}

// 修复机器订单
func (RobotDailyReport) FixRobotOrder(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					RobotName: Robot.RobotName,
				}
			}
		}

		for _, v := range RobotInfoMap {
			RobotDailyReport{}.UpdateOrder(ctx, db, v.RobotName, date)
		}
	}
}

// 修复机器订单交易金额
func (RobotDailyReport) FixRobotTransactionAmount(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					RobotName: Robot.RobotName,
				}
			}
		}

		for _, v := range RobotInfoMap {
			RobotDailyReport{}.UpdateTransactionAmount(ctx, db, v.RobotName, date)
		}
	}
}

// 修复机器曝光量
func (RobotDailyReport) FixRobotFacecount(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					RobotName: Robot.RobotName,
				}
			}
		}

		for _, v := range RobotInfoMap {
			RobotDailyReport{}.UpdateFaceCount(ctx, db, v.RobotName, date)
		}
	}
}

// 机器运营日报更新曝光量
func (RobotDailyReport) UpdateFaceCount(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()
	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" {
		return
	}
	dailyFaceCount := dao.FaceCount{}.GetDailyFaceCount(ctx, dal.Q, robotName, date)
	TotalFaceCount := dao.FaceCountList{}
	TotalFaceCount.FaceCount0 = dailyFaceCount.FaceCount0
	TotalFaceCount.FaceCount1 = dailyFaceCount.FaceCount1
	TotalFaceCount.FaceCount2 = dailyFaceCount.FaceCount2
	TotalFaceCount.FaceCount3 = dailyFaceCount.FaceCount3
	TotalFaceCount.FaceCount = dailyFaceCount.FaceCount0 + dailyFaceCount.FaceCount1 + dailyFaceCount.FaceCount2 + dailyFaceCount.FaceCount3

	provinceID := strconv.Itoa(robotInfo.Areas1)
	cityID := strconv.Itoa(robotInfo.Areas2)
	locationID := strconv.Itoa(robotInfo.Areas3)
	districtID := strconv.Itoa(robotInfo.Areas5)
	dao.RobotDailyReport{}.Save(ctx, db, robotInfo.MerchantID, robotInfo.RobotName, date,
		provinceID, cityID, districtID, locationID,
		dao.RobotDailyReport{}.WithFacecount(common.Decimal(TotalFaceCount.FaceCount, 2)),
		dao.RobotDailyReport{}.WithFacecountFront(common.Decimal(TotalFaceCount.FaceCount0, 2)),
		dao.RobotDailyReport{}.WithFacecountLeft(common.Decimal(TotalFaceCount.FaceCount1, 2)),
		dao.RobotDailyReport{}.WithFacecountRight(common.Decimal(TotalFaceCount.FaceCount2, 2)),
		dao.RobotDailyReport{}.WithFacecountBack(common.Decimal(TotalFaceCount.FaceCount3, 2)),
	)
}

// 修复机器凝视数据
func (RobotDailyReport) FixRobotStarecount(totalStart, totalEnd int64) {
	ctx := context.Background()
	db := mysql.NewDB()

	for i := totalStart; i < totalEnd; i += 86400 {
		date := carbon.CreateFromTimestamp(i).ToDateString()

		var RobotInfoMap = make(map[string]RobotInfo)
		RobotList, robotCount, _ := dao.Robot{}.RobotList(ctx, db, "", date, "", "", "", "", "", "")
		if robotCount == 0 {
			continue
		}
		for _, Robot := range RobotList {
			if Robot.SalesID != "" {
				RobotInfoMap[Robot.RobotName] = RobotInfo{
					RobotName: Robot.RobotName,
				}
			}
		}

		for _, v := range RobotInfoMap {
			RobotDailyReport{}.UpdateStareCount(ctx, db, v.RobotName, date)
		}
	}
}

// 机器运营日报更新凝视
func (RobotDailyReport) UpdateStareCount(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()
	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo.ID == "" {
		return
	}
	dailyStareCount := dao.StareCount{}.GetDailyStareCount(ctx, dal.Q, robotName, date)

	provinceID := strconv.Itoa(robotInfo.Areas1)
	cityID := strconv.Itoa(robotInfo.Areas2)
	locationID := strconv.Itoa(robotInfo.Areas3)
	districtID := strconv.Itoa(robotInfo.Areas5)
	dao.RobotDailyReport{}.Save(ctx, db, robotInfo.MerchantID, robotInfo.RobotName, date,
		provinceID, cityID, districtID, locationID,
		dao.RobotDailyReport{}.WithStarecount(dailyStareCount.StareCount),
		dao.RobotDailyReport{}.WithStarecountFront(dailyStareCount.StareCount0),
		dao.RobotDailyReport{}.WithStarecountLeft(dailyStareCount.StareCount1),
		dao.RobotDailyReport{}.WithStarecountRight(dailyStareCount.StareCount2),
		dao.RobotDailyReport{}.WithStarecountBack(dailyStareCount.StareCount3),
	)
}

// 机器运营日报更新商品点击量
func (RobotDailyReport) UpdateGoodsClick(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()

	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" {
		return
	}
	goodsClickTotal, _ := dao.GoodsClick{}.GetDailyGoodsClick(ctx, db, robotName, "", date)
	var TotalClickNum int64
	for _, v := range goodsClickTotal {
		TotalClickNum += v.ClickNum
	}
	provinceID := strconv.Itoa(robotInfo.Areas1)
	cityID := strconv.Itoa(robotInfo.Areas2)
	locationID := strconv.Itoa(robotInfo.Areas3)
	districtID := strconv.Itoa(robotInfo.Areas5)
	dao.RobotDailyReport{}.Save(ctx, db, robotInfo.MerchantID, robotInfo.RobotName, date,
		provinceID, cityID, districtID, locationID,
		dao.RobotDailyReport{}.WithTotalGoodsClick(TotalClickNum),
	)
}

// 记录屏幕点击量
func (RobotDailyReport) UpdateScreenClick(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()

	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" {
		return
	}
	ScreenClickTotal, _ := dao.PhysicalInfo{}.GetDailyScreenClick(ctx, db, robotName, date)

	provinceID := strconv.Itoa(robotInfo.Areas1)
	cityID := strconv.Itoa(robotInfo.Areas2)
	locationID := strconv.Itoa(robotInfo.Areas3)
	districtID := strconv.Itoa(robotInfo.Areas5)
	dao.RobotDailyReport{}.Save(ctx, db, robotInfo.MerchantID, robotInfo.RobotName, date,
		provinceID, cityID, districtID, locationID,
		dao.RobotDailyReport{}.WithTotalScreenClick(ScreenClickTotal.ScreenClickNum),
	)
}

// 机器运营日报更新订单信息
func (RobotDailyReport) UpdateOrder(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()

	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" || robotInfo.SalesID == "" {
		return
	}
	// 订单
	RobotOrderList, _ := dao.NewOrder(ctx, dal.Q).WithSalesIDCond([]string{robotInfo.SalesID}).WithDateCond(date).RobotOrderOverview()
	repeatUserTotal, _ := dao.WechatRate{}.GetDailyRepeatUser(ctx, db, robotName, "", date)
	for _, order := range RobotOrderList {
		provinceID := order.Areas1
		cityID := order.Areas2
		locationID := order.Areas3
		districtID := order.Areas5
		// 从订单中获取商户id
		dao.RobotDailyReport{}.Save(ctx, db, order.MerchantID, robotInfo.RobotName, date,
			provinceID, cityID, districtID, locationID,
			dao.RobotDailyReport{}.WithGMV(order.GMV),
			dao.RobotDailyReport{}.WithPaymentAmount(order.PaymentAmount),
			dao.RobotDailyReport{}.WithTransactionAmount(order.TransactionAmount),
			dao.RobotDailyReport{}.WithTotalPaidOrder(order.TotalPaidOrder),
			dao.RobotDailyReport{}.WithDiscountAmount(order.DiscountAmount),
			dao.RobotDailyReport{}.WithGiveAmount(order.GiveAmount),
			dao.RobotDailyReport{}.WithTotalOrder(order.TotalOrder),
			dao.RobotDailyReport{}.WithTotalNotPaidOrder(order.TotalNotPaidOrder),
			dao.RobotDailyReport{}.WithTotalPaidUser(repeatUserTotal.PayNum),
		)
	}
	// 退款
	refundList, _ := dao.OrderRefund{}.OrderRefundList(ctx, db, robotName, date)
	if len(refundList) > 0 {
		refundInfo := refundList[0]
		// 查询订单信息
		if strings.Contains(refundInfo.OutTradeNo, "P") {
			refundInfo.OutTradeNo = strings.TrimSuffix(refundInfo.OutTradeNo, "P")
		} else if strings.Contains(refundInfo.OutTradeNo, "F") {
			refundInfo.OutTradeNo = strings.Split(refundInfo.OutTradeNo, "F")[0]
		}
		orderInfo, _ := dao.NewOrder(ctx, dal.Q).WithOutTradeNoCond(refundInfo.OutTradeNo).OrderDetail()
		if orderInfo != nil {
			provinceID := strconv.Itoa(orderInfo.Areas1)
			cityID := strconv.Itoa(orderInfo.Areas2)
			locationID := strconv.Itoa(orderInfo.Areas3)
			districtID := strconv.Itoa(orderInfo.Areas5)
			dao.RobotDailyReport{}.Save(ctx, db, orderInfo.MerchantID, robotInfo.RobotName, date,
				provinceID, cityID, districtID, locationID,
				dao.RobotDailyReport{}.WithRefundAmount(refundInfo.RefundOrderAmount),
				dao.RobotDailyReport{}.WithTotalRefundOrder(refundInfo.RefundOrderCount),
			)
		}
	}
}

// 机器运营日报更新订单交易金额
func (RobotDailyReport) UpdateTransactionAmount(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()

	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" || robotInfo.SalesID == "" {
		return
	}
	// 订单
	RobotOrderList, _ := dao.NewOrder(ctx, dal.Q).WithSalesIDCond([]string{robotInfo.SalesID}).WithDateCond(date).RobotOrderTransactionOverview()
	for _, order := range RobotOrderList {
		provinceID := order.Areas1
		cityID := order.Areas2
		locationID := order.Areas3
		districtID := order.Areas5
		// 从订单中获取商户id
		dao.RobotDailyReport{}.Save(ctx, db, order.MerchantID, robotInfo.RobotName, date,
			provinceID, cityID, districtID, locationID,
			dao.RobotDailyReport{}.WithTransactionAmount(order.TransactionAmount),
		)
	}
}

// 机器运营日报更新任务信息
func (RobotDailyReport) UpdateTask(ctx context.Context, db *gorm.DB, robotName, date string) {
	date = carbon.Parse(date).ToDateString()
	dateStart := carbon.Parse(date).StartOfDay().ToDateTimeString()
	dateEnd := carbon.Parse(date).EndOfDay().ToDateTimeString()

	// 查询机器信息
	robotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", robotName, "")
	if robotInfo == nil || robotInfo.ID == "" {
		return
	}

	// 运营事件
	EventList, _ := dao.Event{}.GetEventList(ctx, db, dateStart, dateEnd)
	EventMap := make(map[string]dao.SalesIDEventList)
	for _, v := range EventList {
		EventMap[v.RobotName] = v
	}
	// 运营数据
	deviceAdminClient, err := client.GetDeviceAdminClient()
	RobotTask := TaskStruct{}
	if err == nil {
		var req devicec.DeviceTaskInfoReq
		req.IdentificationCode = robotInfo.SalesID
		req.Date = date
		res, resErr := deviceAdminClient.DeviceTaskInfo(context.Background(), &req)
		if resErr == nil && res.Code != -1 {
			RobotTask = TaskStruct{
				TotalTaskOdometer:       res.TaskInfo.TotalTaskOdometer,
				TotalTaskDuration:       res.TaskInfo.TotalTaskDuration,
				TotalTaskRunCount:       res.TaskInfo.TotalTaskRunCount,
				TotalChargingDuration:   res.TaskInfo.TotalChargingDuration,
				TotalIdleDuration:       res.TaskInfo.TotalIdleDuration,
				TotalToTaskDuration:     res.TaskInfo.TotalToTaskDuration,
				TotalToChargingDuration: res.TaskInfo.TotalToChargingDuration,
			}
		}
	}
	opt := []dao.RobotDailyReportOption{}
	// 运行时长
	opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskOdometer(RobotTask.TotalTaskOdometer))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskDuration(RobotTask.TotalTaskDuration))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalTaskRunCount(RobotTask.TotalTaskRunCount))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalChargingDuration(RobotTask.TotalChargingDuration))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalIdleDuration(RobotTask.TotalIdleDuration))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalToTaskDuration(RobotTask.TotalToTaskDuration))
	opt = append(opt, dao.RobotDailyReport{}.WithTotalToChargingDuration(RobotTask.TotalToChargingDuration))
	operationDuration := RobotTask.TotalTaskDuration
	if _, ok := EventMap[robotName]; ok {
		operationDuration = RobotTask.TotalTaskDuration - int64(EventMap[robotName].AffectedTimeMill)
		if operationDuration < 0 {
			operationDuration = 0
		}
	}
	opt = append(opt, dao.RobotDailyReport{}.WithTotalOperationDuration(operationDuration))

	provinceID := strconv.Itoa(robotInfo.Areas1)
	cityID := strconv.Itoa(robotInfo.Areas2)
	locationID := strconv.Itoa(robotInfo.Areas3)
	districtID := strconv.Itoa(robotInfo.Areas5)
	// 修复历史数据时，可能merchant_id等信息与当时的机器信息不一致
	dao.RobotDailyReport{}.Save(ctx, db, robotInfo.MerchantID, robotInfo.RobotName, date,
		provinceID, cityID, districtID, locationID,
		opt...,
	)
}

// 数据筛查
// groupType 1机器,2商户,3商场
func (RobotDailyReport) DailyReport(ctx context.Context, loginMerchantID string,
	startTime, endTime int64, merchantIDs, robotID, locationID, cityID, districtID string,
	groupType, dataType int, orderType string, summaryType int,
	page, limit int,
) (data []*models.RobotDailyReport, count int64, summary *models.RobotDailyReport, err error) {
	db := mysql.NewDB()

	merchantIDList, err := common.Order{}.GetMerchantChildren(ctx, loginMerchantID, strings.Split(merchantIDs, ","))
	if err != nil {
		return data, count, nil, err
	}

	list, count, sum, err := dao.NewRobotDailyReport(ctx, db).
		WithTimePeriod(startTime, endTime).
		WithMerchantID(strings.Join(merchantIDList, ",")).
		WithRobotID(robotID).
		WithLocationID(locationID).
		WithCityID(cityID).
		WithDistrictID(districtID).
		WithOrderType(orderType).
		WithGroupType(groupType).
		WithDataType(dataType).
		WithSummary(summaryType).
		WithPaginate(page, limit).
		List()
	if list == nil {
		return []*models.RobotDailyReport{}, 0, summary, err
	}

	// 系统后台grpc
	SystemAdminClient, systemcClientErr := client.GetSystemAdminClient()
	if systemcClientErr != nil {
		logx.Error(ctx, "connect system admin grpc error", logx.Any("err", systemcClientErr.Error()))
		return list, 0, summary, err
	}
	// 设备后台grpc
	DeviceAdminClient, deviceClientErr := client.GetDeviceAdminClient()
	if deviceClientErr != nil {
		logx.Error(ctx, "connect device admin grpc error", logx.Any("err", deviceClientErr.Error()))
		return list, 0, summary, err
	}

	var areas []string
	var locations []string
	for _, v := range list {
		if v.ProvinceID != nil {
			if !lo.Contains(areas, *v.ProvinceID) {
				areas = append(areas, *v.ProvinceID)
			}
		}
		if v.CityID != nil {
			if !lo.Contains(areas, *v.CityID) {
				areas = append(areas, *v.CityID)
			}
		}
		if v.DistrictID != nil {
			if !lo.Contains(areas, *v.DistrictID) {
				areas = append(areas, *v.DistrictID)
			}
		}
		if v.LocationID != nil {
			if !lo.Contains(locations, *v.LocationID) {
				locations = append(locations, *v.LocationID)
			}
		}
	}
	var areaNameMap = make(map[string]string)
	if len(areas) > 0 {
		var req system_admin.AreaNameReq
		req.AreaIds = areas
		res, err1 := SystemAdminClient.AreaName(ctx, &req)
		if err1 != nil {
			logx.Error(ctx, "get area error", logx.Any("res", res), logx.Any("err", err1.Error()))
			return list, 0, summary, err
		}
		areaNameMap = res.AreaNameMap
	}
	var locationNameMap = make(map[string]string)
	if len(locations) > 0 {
		var req devicec.LocationNameReq
		req.LocationIds = locations
		res, err1 := DeviceAdminClient.LocationName(ctx, &req)
		if err1 != nil {
			logx.Error(ctx, "get location error", logx.Any("res", res), logx.Any("err", err1.Error()))
			return list, 0, summary, err
		}
		locationNameMap = res.LocationNameMap
	}
	if summaryType != 0 {
		summary = sum
	}
	var MerchantIDList []string
	for k, v := range list {
		if v.MerchantID != nil {
			if !lo.Contains(MerchantIDList, *v.MerchantID) {
				MerchantIDList = append(MerchantIDList, *v.MerchantID)
			}
		}
		// 点击率 = 点击数/曝光数
		// 总曝光=
		if v.Facecount != nil && v.FacecountLeft != nil && v.FacecountRight != nil && v.FacecountFront != nil {
			*v.Facecount = *v.FacecountLeft + *v.FacecountRight + *v.FacecountFront
			list[k].Facecount = v.Facecount
		}
		if v.Facecount != nil {
			var goodsClickRatio float64
			if *v.Facecount != 0 && v.TotalGoodsClick != nil {
				goodsClickRatio = common.Decimal(float64(*v.TotalGoodsClick)/float64(*v.Facecount), 4)
			}
			list[k].GoodsClickRatio = &goodsClickRatio
		}
		// 支付率 = 支付量/下单量
		if v.TotalOrder != nil {
			var paidRatio float64
			if *v.TotalOrder != 0 && v.TotalPaidOrder != nil {
				paidRatio = common.Decimal(float64(*v.TotalPaidOrder)/float64(*v.TotalOrder), 4)
			}
			list[k].PaidRatio = &paidRatio
		}
		// 创单率 = 下单量/点击量
		if v.TotalGoodsClick != nil {
			var createOrderRatio float64
			if *v.TotalGoodsClick != 0 && v.TotalOrder != nil {
				createOrderRatio = common.Decimal(float64(*v.TotalOrder)/float64(*v.TotalGoodsClick), 4)
			}
			list[k].CreateOrderRatio = &createOrderRatio
		}
		// 退款率 = 退款量/(支付量+退款量)
		if v.TotalPaidOrder != nil {
			var refundRatio float64
			if *v.TotalPaidOrder != 0 && v.TotalRefundOrder != nil {
				refundRatio = common.Decimal(float64(*v.TotalRefundOrder)/(float64(*v.TotalPaidOrder)+float64(*v.TotalRefundOrder)), 4)
			}
			list[k].RefundRatio = &refundRatio
		}
		if v.TransactionAmount != nil && v.RefundAmount != nil {
			paymentAmount := common.Decimal(*v.TransactionAmount-*v.RefundAmount, 4)
			list[k].PaymentAmount = &paymentAmount
		}
		// 省市信息匹配文字
		if v.ProvinceID != nil && len(areaNameMap) > 0 {
			if _, ok := areaNameMap[*v.ProvinceID]; ok {
				province := areaNameMap[*v.ProvinceID]
				list[k].Province = &province
			} else if *v.ProvinceID == "0" {
				province := ""
				list[k].Province = &province
			}
		}
		if v.CityID != nil && len(areaNameMap) > 0 {
			if _, ok := areaNameMap[*v.CityID]; ok {
				city := areaNameMap[*v.CityID]
				list[k].City = &city
			} else if *v.CityID == "0" {
				city := ""
				list[k].City = &city
			}
		}
		if v.DistrictID != nil && len(areaNameMap) > 0 {
			if _, ok := areaNameMap[*v.DistrictID]; ok {
				district := areaNameMap[*v.DistrictID]
				list[k].District = &district
			} else if *v.DistrictID == "0" {
				district := ""
				list[k].District = &district
			}
		}
		if v.LocationID != nil && len(locationNameMap) > 0 {
			if _, ok := locationNameMap[*v.LocationID]; ok {
				location := locationNameMap[*v.LocationID]
				list[k].Location = &location
			} else if *v.LocationID == "0" {
				location := ""
				list[k].Location = &location
			}
		}
	}
	merchantReq := &system_admin.MerchantNameReq{
		MerchantIds: MerchantIDList,
	}
	merchantNameMapRes, err1 := SystemAdminClient.MerchantName(ctx, merchantReq)
	if err1 != nil {
		return list, 0, summary, err
	}
	for k, v := range list {
		if v.MerchantID != nil {
			if merchantNameMapRes != nil && merchantNameMapRes.MerchantNameMap != nil {
				if _, ok := merchantNameMapRes.MerchantNameMap[*v.MerchantID]; ok {
					merchantName := merchantNameMapRes.MerchantNameMap[*v.MerchantID]
					list[k].MerchantName = &merchantName
				}
			}
		}
	}
	if summaryType == 1 {
		// 订单
		// 点击率 = 点击数/曝光数
		if summary.Facecount != nil && summary.FacecountLeft != nil && summary.FacecountRight != nil && summary.FacecountFront != nil {
			*summary.Facecount = *summary.FacecountLeft + *summary.FacecountRight + *summary.FacecountFront
		}
		if summary.Facecount != nil {
			var goodsClickRatio float64
			if *summary.Facecount != 0 && summary.TotalGoodsClick != nil {
				goodsClickRatio = common.Decimal(float64(*summary.TotalGoodsClick)/float64(*summary.Facecount), 4)
			}
			summary.GoodsClickRatio = &goodsClickRatio
		}
		// 支付率 = 支付量/下单量
		if summary.TotalOrder != nil {
			var paidRatio float64
			if *summary.TotalOrder != 0 && summary.TotalPaidOrder != nil {
				paidRatio = common.Decimal(float64(*summary.TotalPaidOrder)/float64(*summary.TotalOrder), 4)
			}
			summary.PaidRatio = &paidRatio
		}
		// 创单率 = 下单量/点击量
		if summary.TotalGoodsClick != nil {
			var createOrderRatio float64
			if *summary.TotalGoodsClick != 0 && summary.TotalOrder != nil {
				createOrderRatio = common.Decimal(float64(*summary.TotalOrder)/float64(*summary.TotalGoodsClick), 4)
			}
			summary.CreateOrderRatio = &createOrderRatio
		}
		// 退款率 = 退款量/(支付量+退款量)
		if summary.TotalPaidOrder != nil {
			var refundRatio float64
			if *summary.TotalPaidOrder != 0 && summary.TotalRefundOrder != nil {
				refundRatio = common.Decimal(float64(*summary.TotalRefundOrder)/(float64(*summary.TotalPaidOrder)+float64(*summary.TotalRefundOrder)), 4)
			}
			summary.RefundRatio = &refundRatio
		}
		if summary.TransactionAmount != nil && summary.RefundAmount != nil {
			paymentAmount := common.Decimal(*summary.TransactionAmount-*summary.RefundAmount, 4)
			summary.PaymentAmount = &paymentAmount
		}
	}
	return list, count, summary, err
}

type RobotActiveRank struct {
	Hours       int   `json:"hours"`
	DeviceCount int64 `json:"count"`
}

func (RobotDailyReport) RobotActiveRank(ctx context.Context, merchantID string, startTime, endTime int64) (data []RobotActiveRank, err error) {
	db := mysql.NewDB()
	merchantIDList, err := common.Order{}.GetMerchantChildren(ctx, merchantID, []string{})
	if err != nil {
		return data, err
	}

	list, err := dao.NewRobotDailyReport(ctx, db).
		WithTimePeriod(startTime, endTime).
		WithMerchantID(strings.Join(merchantIDList, ",")).
		ActiveRobotList()
	var ActiveHourMap = make(map[int]int64)
	for i := 0; i < 16; i++ { // 0-15
		ActiveHourMap[i] = 0
	}
	for _, v := range list {
		hour := *v.TotalTaskDuration / (1000 * 3600)
		if hour >= 15 {
			hour = 15
		}
		ActiveHourMap[int(hour)] = ActiveHourMap[int(hour)] + 1
	}
	for k, v := range ActiveHourMap {
		data = append(data, RobotActiveRank{
			Hours:       k,
			DeviceCount: v,
		})
	}
	sort.Slice(data, func(i, j int) bool {
		return data[i].Hours < data[j].Hours
	})
	return data, err
}
